#ifndef _GUI_
#define _GUI_

#include <vector>
#include <string>

#define LINE_LE_SPEED 3.8f // 1 means 2 secs (1/speed*2)

class __GUI
{
public:
	void SetFocus(int);
	int GetFocus();
	void * GetCtrl(int);
};
class GUIObject
{
public:
	GUIObject()	
	{ 
		Visible = true;
		Enabled = true;
	}
	~GUIObject(){}
	
	void PreInit(__GUI* g, int nid){_parent = g;id = nid; Init();}
	void Focus(){}
	bool GetFocus(){return Gui()->GetFocus() == id;}
	bool KeyClick(int key){}
	
	int id;
	bool Visible;
	bool Enabled;
	bool (*OnClick)(int);
	bool (*OnIntro)();

	__GUI * Gui(){return _parent;}
	GUIObject * Next(){ return (GUIObject*)Gui()->GetCtrl(id + 1);}
	GUIObject * Prev(){ return (GUIObject*)Gui()->GetCtrl(id + 1);}

	virtual void	MouseOver(bool bOver){}
	virtual bool	MouseMove(float x, float y){return false;}
	virtual bool	MouseLButton(bool bDown){return false;}
	virtual bool	MouseRButton(bool bDown){return false;}
	virtual bool	MouseWheel(int nNotches){return false;}
	virtual bool	IsDone(){return true;}
	virtual b2Vec2	Rectangle(){return b2Vec2_zero;}
	virtual b2Vec2	GetPosition(){return b2Vec2_zero;}
	virtual void	Enter(){}
	virtual void	Leave(){}
	virtual void	SetColor(DWORD _color){}
	virtual void	SetPosition(b2Vec2 pos){}
	virtual void	Draw(){}
	virtual void	Update(float dt){}
	virtual void	Init(){}
	virtual void	SetTitle(){}
	virtual void	Reset() {}

protected:
	__GUI* _parent;
};

typedef GUIObject VGuiObject, * PGuiObject;
class DerivedGUI : public __GUI
{
public:
	bool Visible;
	bool Enabled;

	DerivedGUI()
	{
		Objects = 0;
		focus = on = -1;
		Enabled = true;
		Visible = true;
	}
	~DerivedGUI(){};
	void AddCtrl(void * ctrl)
	{
		GUIObject * GO = (GUIObject*) ctrl;
		GO->PreInit(this,Objects);
		ObjectPointers.resize(Objects + 1);
		ObjectPointers[Objects] = (DWORD)ctrl;
		Objects++;
	}
	bool DelCtrl(int id)
	{
		GUIObject * GO;
		for(int i = 0;i<Objects;i++)
		{
			GO = (GUIObject*)ObjectPointers[i];
			if (GO->id == id)
			{
				GO->~GUIObject();
				ObjectPointers[i] = ObjectPointers[Objects];
				ObjectPointers.resize(Objects - 1);
				Objects--;
				return true;
			}
		}
		return false;
	}
	bool DelCtrl(void * ctrl)
	{
		GUIObject * GO;
		for(int i = 0;i<Objects;i++)
		{
			if (ObjectPointers[i] == (DWORD)ctrl)
			{
				GO = (GUIObject*)ctrl;
				GO->~GUIObject();
				ObjectPointers[i] = ObjectPointers[Objects];
				ObjectPointers.resize(Objects - 1);
				Objects--;
				return true;
			}
		}
		return false;
	}
	void * GetCtrl(int id)
	{
		if(id > Objects)
			return (void *)ObjectPointers[0];
		else if (id < 0)
			return (void *)ObjectPointers[Objects - 1];
		else
			return (void *)ObjectPointers[id];
		return NULL;
	}
	void SetCursor(char * path){Cursor.LoadTexture(path);}
	void Update(float dt)
	{
		// Set Mouse position.
		b2Vec2 pos;
		hge->Input_GetMousePos(&pos.x,&pos.y);
		pos.x += Cursor.GetTexSize().x/2;
		pos.y = GAME_HEIGHT - pos.y - Cursor.GetTexSize().y/2;
		Cursor.SetPosition(pos);
		if (!Enabled || !Visible)
		{
			focus = -1;
			return;
		}
		UpdateInput();
		// Draw controls (before draw mouse).
		if(Objects < 1)
			return;
		GUIObject * GO;
		int NowOn = -1;
		for(int i = 0;i<Objects;i++)
		{
			GO = (GUIObject*)ObjectPointers[i];
			if (!GO->Enabled ||!GO->Visible)
			{
				if (on == i)
					GO->Leave();
				continue;
			}

			// Check for enter event:
			if (GetMousePosition().x >= GO->GetPosition().x - GO->Rectangle().x/2 && GetMousePosition().x <= GO->GetPosition().x + GO->Rectangle().x/2 && GetMousePosition().y >= GO->GetPosition().y  - GO->Rectangle().y/2 && GetMousePosition().y <= GO->GetPosition().y + GO->Rectangle().y/2)
			{
				if(on != i)
					GO->Enter();
				NowOn = i;			
			}
			if (on == i && NowOn != on)
				GO->Leave();
			// Update all:
			GO->Update(dt);
		}
		// Focus if click where it is on
		if (hge->Input_KeyDown(HGEK_LBUTTON))
			SetFocus(NowOn);
		on = NowOn;
	}
	void Draw()
	{
		if (!Visible)
			return;
		// Draw background:
		BackGround.Draw();
		// Draw mouse:
		if(Objects < 1)
		{
			if (hge->Input_IsMouseOver())
				Cursor.Draw();
			return;
		}
		GUIObject * GO;
		for(int i = 0;i<Objects;i++)
		{
			GO = (GUIObject*)ObjectPointers[i];
			GO->Draw();
		}
		if (hge->Input_IsMouseOver())
			Cursor.Draw();
	}

	void SetFocus(int id)
	{
		GUIObject * GO;
		if (id == -1)
		{
			if (focus != id)
			{
				focus = id;
				GO = (GUIObject*)GetCtrl(focus);
			}
			return;
		}
		if (focus != id)
		{
			focus = (id < 0)		?	Objects - 1 : id	;
			focus = (id > Objects)	?	0			: focus	;
			GO = (GUIObject*)ObjectPointers[id];
			GO->Focus();
		}
	}
	int	GetFocus(){	return focus; }
	void MoveCtrl(int id, b2Vec2 pos)
	{
		GUIObject * GO = (GUIObject*)GetCtrl(id);
		GO->SetPosition(pos);
	}
	void ShowCtrl(int id, bool Visible)
	{
		GUIObject * GO = (GUIObject*)GetCtrl(id);
		GO->Visible = Visible;
	}
	void EnableCtrl(int id, bool Enabled)
	{
		GUIObject * GO = (GUIObject*)GetCtrl(id);
		GO->Enabled = Enabled;
	}

	void SetColor(DWORD color)
	{
		GUIObject * GO;
		for(int i = 0;i<Objects;i++)
		{
			GO = (GUIObject*)ObjectPointers[i];
			GO->SetColor(color);
		}
	}
	void SetBackG(char * path)
	{
		BackGround.LoadTexture(path);
		BackGround.SetPosition(b2Vec2(FLOAT(GAME_WIDTH/2),FLOAT(GAME_HEIGHT/2)));
	}
	
private:
	std::vector<DWORD> ObjectPointers;
	int Objects;
	int focus;
	int on;
	Sprite Cursor;
	Sprite BackGround;
	
	void UpdateInput()
	{
		if (focus == -1)
			return;
		if (hge->Input_KeyDown(HGEK_TAB))
			this->SetFocus(this->GetFocus() + 1);
		GUIObject * GO = (GUIObject*)ObjectPointers[GetFocus()];
		if (hge->Input_KeyDown(HGEK_ENTER))
			GO->Enter();
	}
};

typedef DerivedGUI VGui, * PGui;
class GUILine : public GUIObject
{
public:
	b2Vec2 GetPosition(){return _first;}
	void SetPosition(b2Vec2 first,b2Vec2 last){_first = first; _last = last;}
	void SetColor(DWORD ncolor){_color = ncolor;}
	void Draw()
	{
		if (!Visible)
			return;
		hge->Gfx_RenderLine(_first.x,GAME_HEIGHT - _first.y,_last.x,GAME_HEIGHT - _last.y,_color);
	}
protected:
	DWORD _color;
	b2Vec2 _first;
	b2Vec2 _last;
};
typedef GUILine VGuiLine, * PGuiLine;
class GUIRectangle : public GUIObject
{
public:
	b2Vec2 GetPosition(){return _pos;}
	void SetPosition(b2Vec2 npos){_pos = npos;}
	void SetColor(DWORD ncolor){_color = ncolor;}
	void SetSize(b2Vec2 nsize){_rect = nsize;}
	void Draw()
	{
		if (!Visible)
			return;
		hge->Gfx_RenderLine(_pos.x - _rect.x/2,GAME_HEIGHT - _pos.y - _rect.y/2,_pos.x + _rect.x/2,GAME_HEIGHT - _pos.y - _rect.y/2,_color); //top
		hge->Gfx_RenderLine(_pos.x - _rect.x/2,GAME_HEIGHT - _pos.y + _rect.y/2,_pos.x + _rect.x/2,GAME_HEIGHT - _pos.y + _rect.y/2,_color); //bottom
		hge->Gfx_RenderLine(_pos.x - _rect.x/2,GAME_HEIGHT - _pos.y - _rect.y/2,_pos.x - _rect.x/2,GAME_HEIGHT - _pos.y + _rect.y/2,_color); //left
		hge->Gfx_RenderLine(_pos.x + _rect.x/2,GAME_HEIGHT - _pos.y - _rect.y/2,_pos.x + _rect.x/2,GAME_HEIGHT - _pos.y + _rect.y/2,_color); //right
	}
protected:
	DWORD _color;
	b2Vec2 _rect;
	b2Vec2 _pos;
};
typedef GUIRectangle VGuiRect, * PGuiRect;
class GUILabel : public GUIObject
{
public:
	int Align;
	void Init()
	{
		_rend = new hgeFont("Fonts\\TimesNR.fnt");
		SetColor(0xFF000000);
		Align = 0;
		DerivedInit();
	}
	virtual void DerivedInit(){} // Used by LabelEntry
	void SetColor(DWORD _color){_rend->SetColor(_color);}
	void SetPosition(b2Vec2 pos){Position = pos;}
	b2Vec2 GetPosition()
	{
		b2Vec2 Fix(0,-Rectangle().y/2);
		if (BitIn(Align,Al_Left))
			Fix.x = Rectangle().x/2;		
		else if (BitIn(Align,Al_Right))
			Fix.x = -Rectangle().x/2;
		return GetPositionAligned() + Fix;
	}
	b2Vec2 GetPositionAligned()
	{
		b2Vec2 align(0,0);
		if (BitIn(Align,Al_Bottom))
			align.y = +_rend->GetHeight();
		else if (BitIn(Align,Al_Middle))
			align.y = +_rend->GetHeight()/2;
		return Position + align;
	}
	b2Vec2 Rectangle(){	return b2Vec2(_rend->GetStringWidth(_text.c_str()),_rend->GetHeight());	}
	void SetText(char * t){_text = t;}
	void Draw(){_rend->Render(GetPositionAligned().x,GAME_HEIGHT - GetPositionAligned().y,(BitIn(Align,Al_Right)) ? 1 : 0 + (BitIn(Align,Al_Center)) ? 2 : 0,_text.c_str());}
//static:

	static const char Al_Left = HGETEXT_LEFT;
	static const char Al_Right = HGETEXT_RIGHT;
	static const char Al_Center = HGETEXT_CENTER;
	static const char Al_Top = HGETEXT_TOP;
	static const char Al_Bottom = HGETEXT_BOTTOM;
	static const char Al_Middle = HGETEXT_MIDDLE;
protected:
	std::string _text;
	b2Vec2 Position;
	hgeFont * _rend;
};
typedef GUILabel VGuiLabel, * PGuiLabel;
class GUILabelEntry : public GUILabel
{
public:
	void DerivedInit()
	{
		PermitedKeys[0] = NULL;
		Limited = false;
		PPos = 0;
		lineEffectDelay = 0;
		Line.SetColor(0xFF00FFFF);
		Rect.SetColor(0xFF000000);
		Rect.Visible = true;
		Line.Visible = false;
	}
	VGui * Gui(){return (VGui*)_parent;}
	void AddChar(char t)
	{
		if (Limited && strfind(PermitedKeys,t) != -1) 
		{
			if(PPos > _text.length())
				PPos = _text.length();
			this->_text.insert(PPos,CharToString(t));
			PPos++;
			LineEffect(true);
		}
	}
	void RemoveLChar()
	{
		if(_text.length() < 1 || PPos < 1)
			return;
		this->_text.replace(PPos - 1,1,"");
		PPos--;
		LineEffect(true);
	}
	void RemoveRChar()
	{
		if(_text.length() < 1 || PPos > _text.length())
			return;
		this->_text.replace(PPos,1,"");
		LineEffect(true);
	}
	void Update(float32 dt)
	{
		if (!Enabled || !Visible)
			return;
		if(Gui()->GetFocus() != id)
		{
			SetColor(0xFF000000);
			lineEffectDelay = 0;
			Line.Visible = false;
			if (_text.length() == 0)
			{
				_text = "   ";
				_empty = true;
			}
			return;
		}
		if (_empty)
			_text = "";
		(lineEffectDelay - dt <= 0) ? LineEffect() : lineEffectDelay -= dt;
		SetColor(0xFFFFFFFF);
		bool SHIFT = hge->Input_GetKeyState(HGEK_SHIFT);
		for (unsigned char c = 0;c<255;c++)
		{
			if(hge->Input_KeyDown(c))
			{
				if(c < HGEK_Z + 1 && c > HGEK_A - 1)
					if(SHIFT)
						AddChar(c);
					else
						AddChar(c + 32);
				if((c < HGEK_9 + 1 && c > HGEK_0 - 1) || (c == HGEK_SPACE))
					AddChar(c);
				if(c < HGEK_NUMPAD9 + 1 && c > HGEK_NUMPAD0 - 1)
					AddChar(c-0x30);
				if(c == HGEK_ENTER)
					AddChar('\n');
				if(c == HGEK_DECIMAL || (c == HGEK_PERIOD && !SHIFT))
					AddChar('.');
				if(c == HGEK_BACKSPACE)
					RemoveLChar();
				if(c == HGEK_DELETE)
					RemoveRChar();
				if (c == HGEK_LEFT)
				{
					(PPos < 1) ? PPos = 0 : PPos--;
					LineEffect(true);
				}
				if (c == HGEK_RIGHT)
				{
					(PPos > _text.length()) ? PPos = PPos : PPos++;
					LineEffect(true);
				}
				if (this->OnClick != 0)
					(OnClick)(c);
			}
		}
		return;
	}
	void Draw()
	{
		if (!Visible)
			return;
		_rend->Render(GetPositionAligned().x,GAME_HEIGHT - GetPositionAligned().y,(BitIn(Align,Al_Right)) ? 1 : 0 + (BitIn(Align,Al_Center)) ? 2 : 0,_text.c_str());
		Rect.SetPosition(GetPosition());
		Rect.SetSize(Rectangle());
		Rect.Draw();
		Line.Draw();
	}
	void SetPermitedKeys(const char * perm, bool allperm = false)
	{
		if (allperm)
			Limited = false;
		else
			Limited = true;
		strcpy(PermitedKeys,perm);
	}
	char * GetPermitedKeys(){return PermitedKeys;}
	const char * GetText(){return _text.c_str();}
protected:
	void LineEffect(bool refresh = false)
	{
		lineEffectDelay = 1/LINE_LE_SPEED*2;
		Line.Visible = (!Line.Visible || refresh);
		Line.SetPosition(
			b2Vec2(
				Position.x + _rend->GetStringWidth(_text.substr(0,PPos).c_str()),
				Position.y - _rend->GetHeight()),
			b2Vec2(
				Position.x + _rend->GetStringWidth(_text.substr(0,PPos).c_str()),
				Position.y));
	}
	char PermitedKeys[256];
	bool Limited;
	DWORD PPos;
	float32 lineEffectDelay;
	VGuiLine Line;
	VGuiRect Rect;
private:
	bool _empty;
};
typedef GUILabelEntry VGuiLabelEntry, * PGuiLabelEntry;
class GUIButton : public GUIObject
{
public:
	GUIButton()
	{
		In = IsClicked = false;
		Position = b2Vec2_zero;
	}
	void Init()
	{
		_bsprClick.LoadTexture("VGUI/Templates/Button/pressed.zbs");
		_bsprNClick.LoadTexture("VGUI/Templates/Button/unpressed.zbs");
		_tit.Init();
		_tit.Align = _tit.Al_Center + _tit.Al_Middle;
	}
	bool MouseLButton(bool Down)
	{
		if(!In && !Down)
		{
			IsClicked = false;
			return false;
		}
		if(In && !Down && IsClicked)
		{
			IsClicked = Down;
			return (OnClick) ? OnClick() : false ;
		}
		IsClicked = Down;
		return false;
	};

	void MouseOver(bool Over){In = Over;}
	void Enter() {MouseOver(true);}
	void Leave() {MouseOver(false);}
	void SetColor(DWORD _color) {_bsprClick.SetColor(_color); _bsprNClick.SetColor(_color);}
	void SetPosition(b2Vec2 pos)
	{
		Position = pos;
		_tit.SetPosition(pos);
	}
	b2Vec2 GetPosition(){return Position;}
	void SetTitle(char * t){_tit.SetText(t);}
	void Draw()
	{
		if (!Visible)
			return;
		_bsprClick.SetPosition(Position);
		_bsprNClick.SetPosition(Position);
		(IsClicked && In) ? _bsprClick.Draw() : _bsprNClick.Draw();
		_tit.Draw();
	}
	void Update(float dt)
	{
		if(!Enabled || !Visible)
			return;
		if(In && hge->Input_KeyDown(HGEK_LBUTTON))
			MouseLButton(true);
		if(hge->Input_KeyUp(HGEK_LBUTTON))
			MouseLButton(false);
	}
	bool (*OnClick)();
	b2Vec2 Rectangle(){return b2Vec2(128,64);}
private:
	bool In, IsClicked;
	Sprite _bsprClick;
	Sprite _bsprNClick;
protected:
	b2Vec2 Position;
	VGuiLabel _tit;
};
typedef GUIButton VGuiButton, *PGuiButton;

#endif
